package com.zego.wawaji.wawaji_client.cmd;

import android.content.res.Resources;
import android.os.CountDownTimer;
import android.text.TextUtils;
import android.util.Log;

import com.google.gson.Gson;
import com.noble.library.basic.log.PLogger;
import com.noble.medialib.notify.MediaNotifyUtils;
import com.zego.wawaji.R;
import com.zego.wawaji.base.module.ModuleMgr;
import com.zego.wawaji.third.zego.ZegoMgr;
import com.zego.wawaji.ui.play.bean.PlayInit;
import com.zego.wawaji.ui.wawa.WawaMgr;
import com.zego.wawaji.wawaji_client.AppTinkerLike;
import com.zego.wawaji.wawaji_client.play.BoardState;
import com.zego.wawaji.wawaji_client.play.PlayStatus;
import com.zego.zegoliveroom.ZegoLiveRoom;
import com.zego.zegoliveroom.entity.ZegoUser;

import java.util.HashMap;
import java.util.Map;

/**
 * Command指令
 * <p>
 * 257 - 房间成员更新（Server-->Client）
 * 258 - 通知某人准备上机, 此时用户可使用 CMD_ABANDON_PLAY 放弃游戏（Server-->Client）
 * 260 - 通知游戏结果（Server-->Client）
 * 272 - 回复收到预约申请，并告知预约结果（Server-->Client）
 * 273 - 回复收到确认上机或者放弃玩游戏指令（Server-->Client）
 * 513 - 预约上机申请（Client-->Server）
 * 515 - 确认上机或者放弃玩游戏，仅在正式开始玩之前发送此指令有效，即在收到服务端的 CMD_GAME_READY 指令时，通过该指令告诉服务端开始玩还是放弃（Client-->Server）
 * 528 - 左移指令（Client-->Server）
 * 529 - 右移指令（Client-->Server）
 * 530 - 前移指令（Client-->Server）
 * 531 - 后移指令（Client-->Server）
 * 532 - 抓指令（Client-->Server）
 */
public class CommandUtil {

    private boolean isLog = false;

    /**
     * 最大重试时间为10s.
     */
    public static final long MAX_RETRY_TIME = 10000;

    public static final long RETRY_INTERVAl = 2000;

    public static final String LOG_TAG = "CommandUtil";

    /**
     * 房间成员更新. 服务端会携带用户头像地址
     */
    static final public int CMD_USER_UPDATE = 0x101;

    /**
     * 预约, Client->Server.
     */
    static final public int CMD_APPLY = 0x201;

    /**
     * {
     * "seq": 1,
     * "cmd": 514,
     * "data": {
     * "time_stamp": 12345
     * },
     * "session_data": {}
     * }
     */
    static final public int CMD_CANCEL_APPOINTMENT = 0x202;   // 取消预约（Client-->Server）

    /**
     * 预约结果 272, Server->Client.
     */
    static final public int CMD_APPLY_RESULT = 0x110;

    /**
     * 取消预约结果
     */
    static final public int CMD_CANCEL_APPOINTMENT_REPLY = 0x112;   // 回复收到取消预约指令（Server-->Client）

    /**
     * 准备游戏 258, Server->Client.
     */
    static final public int CMD_GAME_READY = 0x102;

    /**
     * 确认上机或者取消, Client->Server.
     */
    static final public int CMD_CONFIRM_BOARD = 0x203;

    /**
     * 服务端回复收到确认上机或者放弃玩游戏指令 Server->Client.
     */
    static final public int CMD_CONFIRM_BOARD_REPLY = 0x111;

    /**
     * 左移.
     */
    static final public int CMD_MOVE_LEFT = 0x210;

    /**
     * 右移.
     */
    static final public int CMD_MOVE_RIGHT = 0x211;

    /**
     * 上移.
     */
    static final public int CMD_MOVE_FORWARD = 0x212;

    /**
     * 下移.
     */
    static final public int CMD_MOVE_BACKWARD = 0x213;

    /**
     * 抓.
     */
    static final public int CMD_GRUB = 0x214;

    /**
     * 通知游戏结果, Server->Client.
     */
    static final public int CMD_GAME_RESULT = 0x104;

    /**
     * 确认收到游戏结果, Client->Server,
     */
    static final int CMD_CONFIRM_GAME_RESULT = 0x205;

    private static CommandUtil sInstance;

    private int mSeq;

    private ZegoLiveRoom mZegoLiveRoom;

    private static ZegoUser mAnchors[];

    private static Resources mResources;

    private boolean mConfirmBoard;

    private CountDownTimer mCountDownTimerRetryHttpRequest;

    private CommandUtil() {
        mZegoLiveRoom = ZegoMgr.getInstance().getZegoLiveRoom();
        mResources = AppTinkerLike.context.getResources();

        mSeq = 0;
        mAnchors = new ZegoUser[1];
        mConfirmBoard = false;
        mCountDownTimerRetryHttpRequest = null;
    }

    public static CommandUtil getInstance() {
        if (sInstance == null) {
            synchronized (CommandUtil.class) {
                if (sInstance == null) {
                    sInstance = new CommandUtil();
                }
            }
        }
        return sInstance;
    }

    public void reset() {
        mAnchors = new ZegoUser[1];
        mConfirmBoard = false;
        if (mCountDownTimerRetryHttpRequest != null) {
            mCountDownTimerRetryHttpRequest.cancel();
        }
    }

    public int getSeq() {
        mSeq++;
        int seq = mSeq;
        return seq;
    }

    public int getCurrentSeq() {
        return mSeq;
    }

    public void setAnchor(ZegoUser zegoUser) {
        if (zegoUser == null) return;
        mAnchors[0] = zegoUser;
    }

    public void printLog(String msg) {
        if (!isLog) return;
        Log.i(LOG_TAG, msg);
    }

    public boolean isCommandFromAnchor(String userID) {
        if ((mAnchors[0] != null))
            return !TextUtils.isEmpty(mAnchors[0].userID) && mAnchors[0].userID.equals(userID);
        return false;
    }

    public boolean isConfirmBoard() {
        return mConfirmBoard;
    }

    /**
     * 预约CMD
     */
    public void apply(final OnCommandSendCallback callback) {
        // 未处于 Ended 状态，不进行预约申请
        if (PlayStatus.getCurrentBoardSate() != BoardState.Ended) {
            return;
        }

        if (mZegoLiveRoom == null) return;

        PlayStatus.setCurrentBoardSate(BoardState.Applying);

        Map<String, Object> cmdString = new HashMap<>();
        cmdString.put("cmd", CMD_APPLY);
        cmdString.put("seq", getSeq());

        Map<String, Object> data = new HashMap<>();
        data.put("time_stamp", System.currentTimeMillis());
        data.put("avatar", ModuleMgr.getCenterMgr().getMyInfo().getAvatar());
        data.put("nickname", ModuleMgr.getCenterMgr().getMyInfo().getNickname());

        cmdString.put("data", data);

        Gson gson = new Gson();
        final String msg = gson.toJson(cmdString);

        printLog(mResources.getString(R.string.send_reply, "1") + msg);
        mZegoLiveRoom.sendCustomCommand(mAnchors, msg, (errorCode, roomID) -> printLog(mResources.getString(R.string.rsp_reply, "1") + errorCode));

        // 取消之前的定时器
        if (mCountDownTimerRetryHttpRequest != null) {
            mCountDownTimerRetryHttpRequest.cancel();
        }

        // 预约失败后进行重复预约
        mCountDownTimerRetryHttpRequest = new CountDownTimer(MAX_RETRY_TIME, RETRY_INTERVAl) {
            @Override
            public void onTick(long millisUntilFinished) {
                if (PlayStatus.getCurrentBoardSate() == BoardState.Applying) {
                    // 第一次回调的时间间隔 < 2000ms, 则不处理
                    if ((MAX_RETRY_TIME - millisUntilFinished) > RETRY_INTERVAl) {

                        final int times = 5 - (int) ((millisUntilFinished / RETRY_INTERVAl));
                        printLog(mResources.getString(R.string.send_reply, times + "") + msg);
                        mZegoLiveRoom.sendCustomCommand(mAnchors, msg, (errorCode, roomID) -> printLog(mResources.getString(R.string.rsp_reply, times + "") + errorCode));
                    }
                }
            }

            @Override
            public void onFinish() {
                if (PlayStatus.getCurrentBoardSate() == BoardState.Applying) {
                    PlayStatus.setCurrentBoardSate(BoardState.Ended);
                    if (callback != null) {
                        callback.onSendFail();
                    }
                }
            }
        }.start();
    }

    /**
     * 取消预约
     */
    public void cancelApply() {
        // 不是预约状态，不进行指令发送
        if (PlayStatus.getCurrentBoardSate() != BoardState.WaitingBoard) {
            return;
        }

        PlayStatus.setCurrentBoardSate(BoardState.Ended);
        Map<String, Object> cmdString = new HashMap<>();

        cmdString.put("cmd", CMD_CANCEL_APPOINTMENT);
        cmdString.put("seq", getSeq());

        Map<String, Object> data = new HashMap<>();
        data.put("time_stamp", System.currentTimeMillis());

        cmdString.put("data", data);

        Gson gson = new Gson();
        String msg = gson.toJson(cmdString);

        mZegoLiveRoom.sendCustomCommand(mAnchors, msg, (errorCode, roomID) -> printLog("onSendCustomCommand_cancelApply, errorCode:" + errorCode));
    }

    /**
     * 上机 / 放弃 指令拼接
     */
    public void confirmBoard(int rspSeq, String sessionData, int result, final OnCommandSendCallback callback) {
        if (!(PlayStatus.getCurrentBoardSate() == BoardState.WaitingBoard ||
                PlayStatus.getCurrentBoardSate() == BoardState.WaitingReplay)) {
            return;
        }
        PlayStatus.setCurrentBoardSate(BoardState.ConfirmBoard);

        // 确认是否上机
        mConfirmBoard = (result == 1);

        Map<String, Object> cmdString = new HashMap<>();

        cmdString.put("cmd", CMD_CONFIRM_BOARD);
        cmdString.put("seq", getSeq());

        Map<String, Object> data = new HashMap<>();
        data.put("seq", rspSeq);
        data.put("confirm", result);
        data.put("replay", 0);       // 重新游戏  0: 拒绝/放弃；1：确认
        data.put("time_stamp", System.currentTimeMillis());
        if (!TextUtils.isEmpty(sessionData)) {
            data.put("session_data", sessionData);
        }
        cmdString.put("data", data);

        // 游戏配置
        PlayInit playInit = WawaMgr.playInit;
        if (playInit != null) {
            Map<String, Object> gameInit = new HashMap<>();
            gameInit.put("catch_pr", playInit.getCatchPR());
            gameInit.put("catch_up_rand", playInit.getCatchUpRand());
            gameInit.put("catch_up_const", playInit.getCatchUpConst());
            gameInit.put("catch_top_rand", playInit.getCatchTopRand());
            gameInit.put("catch_top_const", playInit.getCatchTopConst());
            gameInit.put("catch_move_rand", playInit.getCatchMoveRand());
            gameInit.put("catch_move_const", playInit.getCatchMoveConst());
            gameInit.put("catch_big_rand", playInit.getCatchBigRand());
            gameInit.put("catch_big_const", playInit.getCatchBigConst());
            gameInit.put("catch_height_rand", playInit.getCatchHeightRand());
            gameInit.put("catch_height_const", playInit.getCatchHeightConst());
            cmdString.put("game_init", gameInit);
        }

        Gson gson = new Gson();
        final String msg = gson.toJson(cmdString);
        sendConfirmBoardMsg(msg, callback);
    }

    private int times;  // 重复发送次数

    private void sendConfirmBoardMsg(final String msg, final OnCommandSendCallback callback) {
        mZegoLiveRoom.sendCustomCommand(mAnchors, msg, (errorCode, roomID) -> {
            PLogger.d("sendConfirmBoardMsg: 第" + times + "次；errorCode" + errorCode);
            if (PlayStatus.getCurrentBoardSate() != BoardState.ConfirmBoard) return;

            // 发送成功
            if (errorCode == 0) {
                times = 0;
                return;
            }

            // 失败重试
            if (times++ < 5) {
                sendConfirmBoardMsg(msg, callback);
                return;
            }

            // 重试失败
            PlayStatus.setCurrentBoardSate(BoardState.Ended);
            if (callback != null) {
                callback.onSendFail();
            }
            times = 0;
        });
    }

    public void moveLeft() {
        if (PlayStatus.getCurrentBoardSate() != BoardState.Boarding) {
            return;
        }

        Map<String, Object> cmdString = new HashMap<>();

        cmdString.put("cmd", CMD_MOVE_LEFT);
        cmdString.put("seq", getSeq());
        cmdString.put(JsonKey.KEY_GAME_ID, ModuleMgr.getWawaMgr().getGameId());
        cmdString.put(JsonKey.KEY_USER_AUTH, ModuleMgr.getLoginMgr().getCookieVerCode());

        Map<String, Object> data = new HashMap<>();
        data.put("time_stamp", System.currentTimeMillis());

        cmdString.put("data", data);

        Gson gson = new Gson();
        String msg = gson.toJson(cmdString);

        mZegoLiveRoom.sendCustomCommand(mAnchors, msg, (errorCode, roomID) -> printLog("onSendCustomCommand_moveLeft, errorCode:" + errorCode));
    }

    public void moveRight() {
        if (PlayStatus.getCurrentBoardSate() != BoardState.Boarding) {
            return;
        }

        Map<String, Object> cmdString = new HashMap<>();

        cmdString.put("cmd", CMD_MOVE_RIGHT);
        cmdString.put("seq", getSeq());
        cmdString.put(JsonKey.KEY_GAME_ID, ModuleMgr.getWawaMgr().getGameId());
        cmdString.put(JsonKey.KEY_USER_AUTH, ModuleMgr.getLoginMgr().getCookieVerCode());

        Map<String, Object> data = new HashMap<>();
        data.put("time_stamp", System.currentTimeMillis());

        cmdString.put("data", data);

        Gson gson = new Gson();
        String msg = gson.toJson(cmdString);

        mZegoLiveRoom.sendCustomCommand(mAnchors, msg, (errorCode, roomID) -> printLog("onSendCustomCommand_moveRight, errorCode:" + errorCode));
    }

    public void moveForward() {
        if (PlayStatus.getCurrentBoardSate() != BoardState.Boarding) {
            return;
        }

        Map<String, Object> cmdString = new HashMap<>();

        cmdString.put("cmd", CMD_MOVE_FORWARD);
        cmdString.put("seq", getSeq());
        cmdString.put(JsonKey.KEY_GAME_ID, ModuleMgr.getWawaMgr().getGameId());
        cmdString.put(JsonKey.KEY_USER_AUTH, ModuleMgr.getLoginMgr().getCookieVerCode());

        Map<String, Object> data = new HashMap<>();
        data.put("time_stamp", System.currentTimeMillis());

        cmdString.put("data", data);

        Gson gson = new Gson();
        String msg = gson.toJson(cmdString);

        mZegoLiveRoom.sendCustomCommand(mAnchors, msg, (errorCode, roomID) -> printLog("onSendCustomCommand_moveForward, errorCode:" + errorCode));
    }

    public void moveBackward() {
        if (PlayStatus.getCurrentBoardSate() != BoardState.Boarding) {
            return;
        }

        Map<String, Object> cmdString = new HashMap<>();

        cmdString.put("cmd", CMD_MOVE_BACKWARD);
        cmdString.put("seq", getSeq());
        cmdString.put(JsonKey.KEY_GAME_ID, ModuleMgr.getWawaMgr().getGameId());
        cmdString.put(JsonKey.KEY_USER_AUTH, ModuleMgr.getLoginMgr().getCookieVerCode());

        Map<String, Object> data = new HashMap<>();
        data.put("time_stamp", System.currentTimeMillis());

        cmdString.put("data", data);

        Gson gson = new Gson();
        String msg = gson.toJson(cmdString);
        mZegoLiveRoom.sendCustomCommand(mAnchors, msg, (errorCode, roomID) -> printLog("onSendCustomCommand_moveBackward, errorCode:" + errorCode));
    }

    /**
     * @param isVibrator 是否震动提醒
     */
    public void grub(boolean isVibrator, final OnCommandSendCallback callback) {
        if (PlayStatus.getCurrentBoardSate() != BoardState.Boarding) {
            return;
        }
        if (isVibrator)
            MediaNotifyUtils.vibrator(AppTinkerLike.getContext());  // 下抓震动提醒
        PlayStatus.setCurrentBoardSate(BoardState.WaitingGameResult);

        Map<String, Object> cmdString = new HashMap<>();

        cmdString.put("cmd", CMD_GRUB);
        cmdString.put("seq", getSeq());
        cmdString.put(JsonKey.KEY_GAME_ID, ModuleMgr.getWawaMgr().getGameId());
        cmdString.put(JsonKey.KEY_USER_AUTH, ModuleMgr.getLoginMgr().getCookieVerCode());

        Map<String, Object> data = new HashMap<>();
        data.put("time_stamp", System.currentTimeMillis());

        cmdString.put("data", data);

        Gson gson = new Gson();
        final String msg = gson.toJson(cmdString);

        printLog(mResources.getString(R.string.send_grub, "1") + msg);
        mZegoLiveRoom.sendCustomCommand(mAnchors, msg, (errorCode, roomID) -> printLog(mResources.getString(R.string.rsp_grub, "1") + errorCode));
    }

    public void confirmGameResult(int rspSeq, String sessionData) {
        Map<String, Object> cmdString = new HashMap<>();

        cmdString.put("cmd", CMD_CONFIRM_GAME_RESULT);
        cmdString.put("seq", getSeq());

        Map<String, Object> data = new HashMap<>();
        data.put("seq", rspSeq);
        data.put("time_stamp", System.currentTimeMillis());
        if (!TextUtils.isEmpty(sessionData)) {
            data.put("session_data", sessionData);
        }

        cmdString.put("data", data);

        Gson gson = new Gson();
        final String msg = gson.toJson(cmdString);

        printLog("sendCustomCommand_confirmGameResult, msg: " + msg);
        mZegoLiveRoom.sendCustomCommand(mAnchors, msg, (errorCode, roomID) -> printLog("onSendCustomCommand_confirmGameResult, errorCode:" + errorCode));
    }

    public interface OnCommandSendCallback {
        void onSendFail();
    }
}
